package is.origo.portlet;

import is.origo.portlet.events.*;
import org.w3c.dom.*;

import javax.portlet.*;
import javax.xml.namespace.*;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import java.io.*;
import java.util.*;


/**
 * @author vidar.svansson
 */
public class GenericEventDispatcherPortlet extends GenericPortlet {

    private GenericPortlet dispatch;
    private static final String PORTLET_CLASS = "portletClass";
    private Set supportedEvents;

    private static class PortletNamespaceContext
            implements NamespaceContext {

        public String getNamespaceURI(String prefix) {
            if (prefix == null)
                throw new NullPointerException("Null prefix");
            if ("p".equals(prefix))
                return "http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd";
            if ("xml".equals(prefix))
                return "http://www.w3.org/XML/1998/namespace";
            else
                return "";
        }

        public String getPrefix(String namespaceURI) {
            throw new UnsupportedOperationException();
        }

        public Iterator getPrefixes(String namespaceURI) {
            throw new UnsupportedOperationException();
        }

        private PortletNamespaceContext() {
        }

    }


    public GenericEventDispatcherPortlet() {
    }

    public void init(PortletConfig portletConfig)
            throws PortletException {

        super.init(portletConfig);


        if (supportedEvents == null) {
            supportedEvents = getSupportedEvents(getPortletName(), portletConfig.getPortletContext().getRealPath("/WEB-INF/portlet1.xml"));
        }

        try {
            Class portletClass = Class.forName(portletConfig.getInitParameter(PORTLET_CLASS));
            dispatch = (GenericPortlet) portletClass.newInstance();
            dispatch.init(portletConfig);
        }
        catch (ClassNotFoundException e) {
            throw new PortletException(PORTLET_CLASS + "  not set or not found", e);
        }
        catch (IllegalAccessException e) {
            throw new PortletException(PORTLET_CLASS + " could not be instatiated, illegal access", e);
        }
        catch (InstantiationException e) {
            throw new PortletException(PORTLET_CLASS + " could not be instatiated", e);
        }


    }

    public void processEvent(EventRequest eventRequest, EventResponse eventResponse)
            throws PortletException, IOException {
        dispatch.processEvent(eventRequest, eventResponse);
    }

    public void processAction(ActionRequest actionRequest, ActionResponse actionResponse)
            throws PortletException, IOException {
        Events.events.set(new HashMap<QName, Serializable>());
        ActionResponse response = new EventActionResponse(actionResponse);
        Events.actionResponse.set(response);
        dispatch.processAction(actionRequest, response);
    }

    public void render(RenderRequest renderRequest, RenderResponse renderResponse)
            throws PortletException, IOException {
        Map<QName, Serializable> events = Events.events.get();
        if (events != null && supportedEvents.size() > 0) {

            for (QName name : events.keySet()) {
                String ns = name.getNamespaceURI().trim();
                QName hax = new QName(ns, name.getLocalPart());
                if (supportedEvents.contains(hax)) {
                    javax.portlet.Event event = new SimpleEvent(name, events.get(name));
                    EventRequest eventRequest = new SimpleEventRequest(renderRequest, event);
                    EventResponse eventResponse = new SimpleEventResponse(renderResponse);
                    processEvent(eventRequest, eventResponse);
                }
            }
        }
        dispatch.render(renderRequest, renderResponse);
    }

    public void destroy() {
        dispatch.destroy();
        super.destroy();
    }

    private Set getSupportedEvents(String name, String path) {
        String query = (new StringBuilder()).append("//p:portlet[p:portlet-name = '").append(name).append("']/p:supported-processing-event").toString();
        Set<QName> supportedEvents = new HashSet<QName>();
        File portletXml = new File(path);
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        XPathFactory xfactory = XPathFactory.newInstance();
        XPath xpath = xfactory.newXPath();
        xpath.setNamespaceContext(new PortletNamespaceContext());

        try {
            XPathExpression expr = xpath.compile(query);
            DocumentBuilder builder = factory.newDocumentBuilder();
            FileInputStream fis = new FileInputStream(portletXml);
            org.w3c.dom.Document doc = builder.parse(fis);
            NodeList result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
            for (int i = 0; i < result.getLength(); i++) {
                Node node = result.item(i);

                NamedNodeMap map = node.getAttributes();
                String ns = null;
                Node xmlns = map.getNamedItem("xmlns:x");
                if (xmlns != null) {
                    ns = xmlns.getNodeValue();
                }

                NodeList events = node.getChildNodes();
                for (int e = 0; e < events.getLength(); e++) {
                    Node nameNode = events.item(e);
                    String nodeName = nameNode.getNodeName();
                    if ("qname".equals(nodeName)) {
                        NamedNodeMap qnameMap = nameNode.getAttributes();
                        if (ns == null) {
                            ns = qnameMap.getNamedItem("xmlns:x").getNodeValue().trim();
                        }
                        String value = nameNode.getChildNodes().item(0).getTextContent();
                        QName qname = new QName(ns, value.substring(2));
                        supportedEvents.add(qname);
                    }
                }
            }
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
        return supportedEvents;
    }
}

